/*
 Copyright 2009 Abdulla Abdurakhmanov (abdulla@latestbit.com)
 Original sources are available at www.latestbit.com

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 SmartBus.cpp. Created on: 17.09.2009 16:11:44
 */

#include "smartobjects/SmartBus.hpp"
#include "smartobjects/network/ManagedNetwork.hpp"
#include "smartobjects/network/EndpointLocation.hpp"
#include "smartobjects/network/StdEndpointCoordinatorFactory.hpp"
#include "smartobjects/coders/StdDataTypesCoder.hpp"
#include "smartobjects/coders/StdPacketFormerFactory.hpp"
#include "smartobjects/encrypters/StdDataEncrypterFactory.hpp"
#include "smartobjects/network/StdHandshakeManagerFactory.hpp"
#include "smartobjects/core/ServerInstancesRegistry.hpp"

namespace smartobjects {

const unsigned int SmartBus::DEFAULT_CONNECT_TIME = 10;

const unsigned int SmartBus::DEFAULT_THREAD_POOL_INITIAL_CNT = 2;
const unsigned int SmartBus::DEFAULT_THREAD_POOL_MAX_CNT = 32;
const std::string SmartBus::SMART_BUS_THREAD_POOL_ID = "SMART_BUS_THREAD_POOL";


SmartBus::SmartBus() :
		m_poManagedNetwork( new ManagedNetwork() ),
		m_poDataEncrypterFactory ( new StdDataEncrypterFactory() ),
		m_poHandshakeManagerFactory ( NULL ),
		m_poPacketFormerFactory ( new StdPacketFormerFactory() ),
		m_poEndpointCoordinatorFactory ( new StdEndpointCoordinatorFactory() ),
		m_poInstanceRegistry ( new ServerInstancesRegistry() ),
		m_poThreadPool(
				new cpputils::ept::ThreadPool(
						DEFAULT_THREAD_POOL_INITIAL_CNT,
						DEFAULT_THREAD_POOL_MAX_CNT, SMART_BUS_THREAD_POOL_ID) ),
		m_oSequenceGenerator(1 /* Initial value */, 1 /* Min Value */, 0x0FFFFFFF /* Max Value */),
		m_nConnectMode ( ISmartBus::SYNC_CONNECT_MODE ),
		m_nAwaitConnectTime (SmartBus::DEFAULT_CONNECT_TIME) {
	m_poHandshakeManagerFactory = new StdHandshakeManagerFactory ( this );
	registerStdManagerFactories();
}

SmartBus::~SmartBus() {
	releaseAndClear(m_poInstanceRegistry);
	releaseAndClear(m_poManagedNetwork);

	boost::unique_lock<boost::recursive_mutex> oClientsLock(m_mxClients);
	BusClientsMap mTmpClients = m_mClients;
	m_mClients.clear();
	oClientsLock.unlock();

	for( BusClientsMap::right_iterator it = mTmpClients.right.begin(); it != mTmpClients.right.end(); it++ ) {
		it->first->release();
	}

	boost::unique_lock<boost::recursive_mutex> oServersLock(m_mxServers);
	BusServersMap mTmpServers = m_mServers;
	m_mServers.clear();
	oServersLock.unlock();

	for( BusServersMap::right_iterator it = mTmpServers.right.begin(); it != mTmpServers.right.end(); it++ ) {
		it->first->release();
	}

	releaseAndClear(m_poDataEncrypterFactory);
	releaseAndClear(m_poHandshakeManagerFactory);
	releaseAndClear(m_poPacketFormerFactory);
	releaseAndClear(m_poEndpointCoordinatorFactory);
	releaseAndClear(m_poThreadPool);
}

void SmartBus::registerStdManagerFactories() {
	m_poManagedNetwork->getNetworkFactory()->setHandshakeManagerFactory(m_poHandshakeManagerFactory);
	m_poManagedNetwork->getNetworkFactory()->setPacketFormerFactory(m_poPacketFormerFactory);
}

IDataTypesCoder* SmartBus::createDataTypesCoder() {
	return new StdDataTypesCoder();
}

cpputils::utils::SequenceGenerator<long>& SmartBus::getSequenceGenerator() {
	return m_oSequenceGenerator;
}

IManagedNetwork* SmartBus::getManagedNetwork() {
	return m_poManagedNetwork;
}

void SmartBus::setManagedNetwork(IManagedNetwork* poNetwork) {
	setNewRefObject(m_poManagedNetwork, poNetwork);
	registerStdManagerFactories();
}

void SmartBus::setConnectMode(ISmartBus::ConnectMode nMode) {
	m_nConnectMode = nMode;
}

ISmartBus::ConnectMode SmartBus::getConnectMode() {
	return m_nConnectMode;
}

void SmartBus::setSyncConnectTimeout(unsigned long nSeconds) {
	m_nAwaitConnectTime = nSeconds;
}

unsigned long SmartBus::getSyncConnectTimeout() {
	return m_nAwaitConnectTime;
}

cpputils::ept::ThreadPool* SmartBus::getThreadPool() {
	return m_poThreadPool;
}

IServerInstancesRegistry* SmartBus::getServerInstancesRegistry() {
	return m_poInstanceRegistry;
}

SmartBusClient* SmartBus::selectBusClient(IEndpointLocation* poLocation) {
	SmartBusClient* poResult = NULL;
	boost::unique_lock<boost::recursive_mutex> oClientsLock(m_mxClients);
	std::string sRef;
	poLocation->toStringReference(sRef);
	BusClientsMap::left_iterator it = m_mClients.left.find(sRef);
	if ( it != m_mClients.left.end() ) {
		poResult = it->second;
		// it->info++;
		poResult->addRef();
	}
	else {
		boost::intrusive_ptr<IEndpointCoordinator> poEndpointCoordinator (
				m_poEndpointCoordinatorFactory->createCoordinator(poLocation->getCoordinator()),
				false
		);
		boost::intrusive_ptr<SmartBusClient> poNewClient (
				new SmartBusClient(this, m_poManagedNetwork ),
				false
		);

		poNewClient->getManagedClient()->setEndpointLocation(poLocation, poEndpointCoordinator.get());
		poNewClient->addRef();
		poResult= poNewClient.get();

		m_mClients.insert( BusClientsMap::value_type(sRef, poResult, 1) );

	}
	return poResult;
}

void SmartBus::returnBusClient(SmartBusClient* poClient) {
	boost::unique_lock<boost::recursive_mutex> oClientsLock(m_mxClients);
	BusClientsMap::right_iterator it = m_mClients.right.find(poClient);
	if ( it != m_mClients.right.end() ) {
		m_mClients.right.erase(it);
	}
}

SmartBusServer* SmartBus::selectBusServer(IEndpointLocation* poLocation) {
	SmartBusServer* poResult = NULL;
	boost::unique_lock<boost::recursive_mutex> oServersLock(m_mxServers);
	std::string sRef;
	poLocation->toStringReference(sRef);
	BusServersMap::left_iterator it = m_mServers.left.find(sRef);
	if ( it != m_mServers.left.end() ) {
		poResult = it->second;
		// it->info++;
		poResult->addRef();
	}
	else {
		boost::intrusive_ptr<IEndpointCoordinator> poEndpointCoordinator (
				m_poEndpointCoordinatorFactory->createCoordinator(poLocation->getCoordinator()),
				false
		);

		boost::intrusive_ptr<SmartBusServer> poNewServer (
				new SmartBusServer(this, m_poManagedNetwork ),
				false
		);
		poNewServer->getManagedServer()->setEndpointLocation(poLocation, poEndpointCoordinator.get());
		poNewServer->addRef();
		poResult= poNewServer.get();
		m_mServers.insert( BusServersMap::value_type(sRef, poResult, 1) );
	}
	return poResult;
}

void SmartBus::returnBusServer(SmartBusServer* poServer) {
	boost::unique_lock<boost::recursive_mutex> oServersLock(m_mxServers);
	BusServersMap::right_iterator it = m_mServers.right.find(poServer);
	if ( it != m_mServers.right.end() ) {
		m_mServers.right.erase(it);
	}
}

IEndpointCoordinatorFactory* SmartBus::getCoordinatorFactory() {
	return m_poEndpointCoordinatorFactory;
}

void SmartBus::setCoordinatorFactory(IEndpointCoordinatorFactory* poFactory) {
	setNewRefObject(m_poEndpointCoordinatorFactory, poFactory);
}

IDataEncrypterFactory* SmartBus::getDataEncrypterFactory() {
	return m_poDataEncrypterFactory;
}

void SmartBus::setDataEncrypterFactory(IDataEncrypterFactory* poFactory) {
	setNewRefObject(m_poDataEncrypterFactory, poFactory);
}

void SmartBus::setHandshakeManagerFactory( IHandshakeManagerFactory* poHandshakeManagerFactory ) {
	setNewRefObject(m_poHandshakeManagerFactory, poHandshakeManagerFactory);
	registerStdManagerFactories();
}

IHandshakeManagerFactory* SmartBus::getHandshakeManagerFactory() {
	return m_poHandshakeManagerFactory;
}

IEndpointLocation* SmartBus::createEndpointLocation(const std::string& sReferenceURI) {
	return new EndpointLocation(this, sReferenceURI);
}

}
